package igmp

import (
	"bytes"
	"encoding/binary"
	"net"

	"gitee.com/sillyman/netutils/macaddr"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"

	"gitee.com/sillyman/pcaphelper/pktbuilder"
)

// BuildIGMPv2ReportWithVLAN 构造IGMPv2报告
func BuildIGMPv2ReportWithVLAN(src net.HardwareAddr, vID uint16, srcIP, dstIP net.IP) []byte {
	if dstIP == nil || !dstIP.IsMulticast() {
		return nil
	}

	v2Report := &layers.IGMPv1or2{
		Type:            layers.IGMPMembershipReportV2,
		MaxResponseTime: 0,
		GroupAddress:    dstIP,
		Version:         2,
	}

	return pktbuilder.BuildingEtherPkt(src, macaddr.MappingMulticastIP4ToMAC(dstIP),
		pktbuilder.GenLayerThatDot1Q(0, vID,
			pktbuilder.GenLayerThatIP4(srcIP, dstIP,
				pktbuilder.GenOtherLayer(
					uint16(layers.IPProtocolIGMP),
					gopacket.Payload(toBytesFromIGMPv1or2(v2Report)),
				),
			),
		),
	)
}

// toBytesFromIGMPv1or2
func toBytesFromIGMPv1or2(v2 *layers.IGMPv1or2) []byte {
	v2.GroupAddress = v2.GroupAddress.To4()

	buf := new(bytes.Buffer)

	// check sum
	binary.Write(buf, binary.BigEndian, v2.Type)     // type
	buf.WriteByte(0x0)                               // max resp time
	binary.Write(buf, binary.BigEndian, v2.Checksum) // checksum
	binary.Write(buf, binary.BigEndian, v2.GroupAddress)
	v2.Checksum = checkSum(buf.Bytes())
	buf.Reset()

	binary.Write(buf, binary.BigEndian, v2.Type)     // type
	buf.WriteByte(0x0)                               // max resp time
	binary.Write(buf, binary.BigEndian, v2.Checksum) // checksum
	binary.Write(buf, binary.BigEndian, v2.GroupAddress)
	return buf.Bytes()
}

func checkSum(data []byte) uint16 {
	var (
		sum    uint32
		length int = len(data)
		index  int
	)
	for length > 1 {
		sum += uint32(data[index])<<8 + uint32(data[index+1])
		index += 2
		length -= 2
	}
	if length > 0 {
		sum += uint32(data[index])
	}
	sum += (sum >> 16)
	return uint16(^sum)
}
